feat(frontend): Support for UDF Ui Parameter#4268
feat(frontend): Support for UDF Ui Parameter#4268carloea2 wants to merge 54 commits intoapache:mainfrom
Conversation
…into feat/ui-parameter
…into feat/ui-parameter
|
@aglinxinyuan Please review it. |
There was a problem hiding this comment.
Pull request overview
This PR adds UDF UI Parameter support, allowing users to declare typed parameters in their Python UDF open() method via self.UiParameter(name=..., type=AttributeType....). The frontend parses these from the code, displays them as an editable properties panel, and the backend injects the UI-supplied values into the Python code before execution.
Changes:
- Frontend: New
UiUdfParametersParserServiceandUiUdfParametersSyncServicethat parseself.UiParameter(...)calls from Python code and sync parsed parameters to the operator property store; newUiUdfParametersComponentrenders the parameters table in the property panel. - Backend (Scala): New
UiUDFParametermodel andPythonUdfUiParameterInjectorthat injects a_texera_injected_ui_parametershook method into the UDF class with the UI-supplied values encoded via thepybuildermechanism; all three Python UDF operator descriptors updated to run the injector. - Backend (Python): New
_UiParameterSupportmixin class added to all UDF base classes; it wrapsopen()via__init_subclass__to apply injected values before user code runs.
Reviewed changes
Copilot reviewed 24 out of 25 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
workflow-compiling.interface.ts |
Adds large_binary to AttributeType, JAVA/Python attribute type name constants, and derived union types for cross-language type token handling |
ui-udf-parameters-parser.service.ts |
New service that parses self.UiParameter(...) calls from Python code and normalizes type tokens to canonical schema names |
ui-udf-parameters-parser.service.spec.ts |
Tests for the parser service; contains incorrect expected type values |
ui-udf-parameters-sync.service.ts |
New service that attaches to YText changes and syncs parsed parameter structure to operator properties |
ui-udf-parameters.component.* |
New Angular Formly custom type component rendering the parameters table with read-only name/type and editable value columns |
operator-property-edit-frame.component.ts/.scss |
Subscribes to param changes and maps the uiParameters field key to the custom Formly type; adds styling |
code-editor.component.ts |
Attaches/detaches the YText listener when the Monaco editor is initialized/destroyed |
formly-config.ts / app.module.ts |
Registers the new ui-udf-parameters Formly type and declares the component |
PythonUdfUiParameterInjector.scala |
New Scala object that injects the _texera_injected_ui_parameters hook method into UDF classes |
PythonUdfUiParameterInjectorSpec.scala |
Tests for the Scala injector; contains failing test assertions |
UiUDFParameter.scala |
New Scala model class for a UI parameter (attribute + string value) |
PythonUDFOpDescV2.scala / DualInputPortsPythonUDFOpDescV2.scala / PythonUDFSourceOpDescV2.scala |
Adds uiParameters field and wires the injector call |
Attribute.java |
Adds @EncodableStringAnnotation to getName() for safe encoding in pybuilder templates; introduces unused imports |
udf_operator.py |
Adds _UiParameterSupport mixin with UiParameter inner class and wrapping mechanism |
attribute_type.py |
Adds FROM_STRING_PARSER_MAPPING for string-to-type conversion |
pytexera/__init__.py / pyamber/__init__.py |
Exports AttributeType |
collab-wrapper.component.css |
Minor whitespace change to an already-invalid CSS property |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
aglinxinyuan
left a comment
There was a problem hiding this comment.
Please fix the test cases, as the frontend currently fails to compile. Also address the comments left by Copilot. The PR still feels quite draft-like, so please take some time to polish it before requesting for review.
|
@aglinxinyuan please continue with the review |
aglinxinyuan
left a comment
There was a problem hiding this comment.
LGTM in general. Since it's a huge PR, let's have a secondary reviewer on this. @kunwp1, please review it.
Since it's a huge PR, let's have a secondary reviewer on this.
There was a problem hiding this comment.
Thanks for the PR! I haven't completed the full code review yet, but I have a few observations regarding the user experience and some edge-case behavior:
-
Documentation for UI Parameters: Since the new UI parameter introduces a bit of complexity on editing the UDF code, it might be difficult for users to add a parameter without guidance. Could you add some docstrings or comments to the Python UDF operators explaining how to use this parameter?
-
Data Type Restrictions (BINARY / LARGE_BINARY): I am concerned about the usefulness of supporting BINARY and LARGE_BINARY types here. Since the property panel relies on text input rather than file uploads, providing a raw byte array manually is pretty bad experience for a user. Should we disable these types for now? If you have a specific use case in mind where manual hex/binary input is necessary, let's discuss!
-
UI Sync Bug: I noticed that if a parameter is commented out in the code, it still persists in the property panel. We should ensure the UI stays in sync with the active code state. Could you take a look at the parsing logic for that?
|
Thanks.
|
|
@carloea2 Please resolve conflicts. |
|
@chenlica I will fix it after the current code pass the review, that is more efficient I think. |
Signed-off-by: Xinyuan Lin <xinyual3@uci.edu>
Signed-off-by: Xinyuan Lin <xinyual3@uci.edu>
Signed-off-by: Xinyuan Lin <xinyual3@uci.edu>
I just did the fix since it's caused by my changes. It's usually easier to resolve conflicts earlier than later. |
Ok, thanks. |
|
I removed support for Binary/Large Binary Thanks. |
kunwp1
left a comment
There was a problem hiding this comment.
Left some comments. I tested the PR and the features look good. We need a architecture diagram do the review because the change is very big and I don't follow the details of the implementation. Can you add a diagram in the PR?
| "Iterator", | ||
| "Optional", | ||
| "Union", | ||
| "Dict", |
There was a problem hiding this comment.
No, this change is needed for the feature to work. The reason is the definitions of UiParameter use those as the return value types, so since the code comes from the User, I make them available from the import.
There was a problem hiding this comment.
@kunwp1 NameError: name 'Dict' is not defined
2026-04-18 12:17:21.586 | ERROR | core.architecture.rpc.async_rpc_server:receive:102 - name 'Dict' is not defined
File "C:\Users\carlo\AppData\Local\Temp\tmpkqz8h60kfsTempFS\udf-v1.py", line 12, in
class ProcessTupleOperator(UDFOperatorV2):
-> <class 'pytexera.udf.udf_operator.UDFOperatorV2'>
File "C:\Users\carlo\AppData\Local\Temp\tmpkqz8h60kfsTempFS\udf-v1.py", line 30, in ProcessTupleOperator
def _texera_injected_ui_parameters(self) -> Dict[str, Any]:
NameError: name 'Dict' is not defined
|
@kunwp1 thanks for the review, I will let you know when the diagram is ready, though I think it may not reflect the changes, since the architecture is the same, just changed the way the UDFs contents are generated. |
|
@kunwp1 are these diagrams ok?: Overall: sequenceDiagram
autonumber
participant FE as Frontend UI
participant Parser as Parser Service
participant Store as Operator Properties
participant BE as Scala Backend
participant Inj as Python Code Injector
participant Py as Python Runtime
participant UDF as User UDF
FE->>Parser: Send Python code
Parser->>Parser: Extract self.UiParameter declarations
Parser-->>FE: Return parameter schema
FE->>Store: Save uiParameters<br/>name, type, value
Store->>BE: Submit operator descriptor
BE->>Inj: Pass original code + uiParameters
Inj->>Inj: Validate names and types
Inj->>Inj: Generate injected hook
Inj-->>BE: Return modified Python code
BE->>Py: Execute injected code
Py->>UDF: Load UDF class
UDF->>Py: open() is wrapped
Py->>UDF: Call injected hook
UDF-->>Py: Return parameter values
Py->>UDF: Provide typed UiParameter values
UDF->>Py: Execute workflow logic
Detailed: sequenceDiagram
autonumber
actor User
participant CodeEditor as CodeEditorComponent<br/>Monaco editor
participant YCode as Yjs shared code
participant Sync as UiUdfParametersSyncService
participant Parser as UiUdfParametersParserService
participant ParamUI as UiUdfParametersComponent
participant Formly as Formly / Operator Properties
participant OpDesc as Python UDF Operator Descriptor<br/>Scala backend
participant Injector as PythonUdfUiParameterInjector
participant Runtime as Python Runtime
participant Support as _UiParameterSupport
participant UDF as User UDF Class
User->>CodeEditor: Writes Python UDF code
CodeEditor->>YCode: Updates shared code text
YCode->>Sync: Emits code-change event
Sync->>Parser: Parse latest Python code
Parser->>Parser: Find supported UDF class
Parser->>Parser: Scan self.UiParameter(...)
Parser-->>Sync: Return inferred parameters<br/>name + AttributeType
Sync->>Formly: Update operator uiParameters structure
Formly->>ParamUI: Render parameter rows
ParamUI-->>User: Shows inferred params<br/>name/type locked, value editable
User->>ParamUI: Enters parameter values
ParamUI->>Formly: Store edited values
Formly->>OpDesc: Persist uiParameters in operator properties
User->>OpDesc: Runs workflow
OpDesc->>Injector: inject(code, uiParameters)
Injector->>Injector: Validate uiParameters
Injector->>Injector: Build parameter map
Injector->>Injector: Generate reserved hook method
Injector->>Injector: Insert hook into user UDF class
Injector-->>OpDesc: Return injected Python code
OpDesc->>Runtime: Execute OpExecWithCode(injectedCode, "python")
Runtime->>UDF: Load user UDF class
UDF->>Support: __init_subclass__ wraps open()
Runtime->>UDF: Start operator execution
UDF->>Support: wrapped open() runs first
Support->>UDF: Call _texera_injected_ui_parameters()
UDF-->>Support: Return UI parameter values
Support->>Support: Store injected values
Support->>UDF: Continue original user open()
UDF->>Support: Create/read self.UiParameter(...)
Support->>Support: Look up injected value by name
Support->>Support: Convert value using AttributeType
Support-->>UDF: Return typed Python value
UDF->>Runtime: Process tuples / batches / source output
|
|
I haven't read the details of the diagram yet but will do it later after all the comments are addressed. Please move the diagrams to the PR description. |
|
@kunwp1 |
What changes were proposed in this PR?
Add Python UDF Parameter support:

What the user writes (Python)
Users declare UI parameters once in
open(), and then use the typed value directly:What shows up in the UI
From those
self.UiParameter(...)lines, the property panel automatically generates a Parameters section with one row per parameter:How the values get into Python
When the workflow runs, we inject the UI values into the UDF and the base class applies them right before
open()executes. That way, when the user callsUiParameter(...).value, they get the current value from the UI.Overall:
sequenceDiagram autonumber participant FE as Frontend UI participant Parser as Parser Service participant Store as Operator Properties participant BE as Scala Backend participant Inj as Python Code Injector participant Py as Python Runtime participant UDF as User UDF FE->>Parser: Send Python code Parser->>Parser: Extract self.UiParameter declarations Parser-->>FE: Return parameter schema FE->>Store: Save uiParameters<br/>name, type, value Store->>BE: Submit operator descriptor BE->>Inj: Pass original code + uiParameters Inj->>Inj: Validate names and types Inj->>Inj: Generate injected hook Inj-->>BE: Return modified Python code BE->>Py: Execute injected code Py->>UDF: Load UDF class UDF->>Py: open() is wrapped Py->>UDF: Call injected hook UDF-->>Py: Return parameter values Py->>UDF: Provide typed UiParameter values UDF->>Py: Execute workflow logicDetailed:
sequenceDiagram autonumber actor User participant CodeEditor as CodeEditorComponent<br/>Monaco editor participant YCode as Yjs shared code participant Sync as UiUdfParametersSyncService participant Parser as UiUdfParametersParserService participant ParamUI as UiUdfParametersComponent participant Formly as Formly / Operator Properties participant OpDesc as Python UDF Operator Descriptor<br/>Scala backend participant Injector as PythonUdfUiParameterInjector participant Runtime as Python Runtime participant Support as _UiParameterSupport participant UDF as User UDF Class User->>CodeEditor: Writes Python UDF code CodeEditor->>YCode: Updates shared code text YCode->>Sync: Emits code-change event Sync->>Parser: Parse latest Python code Parser->>Parser: Find supported UDF class Parser->>Parser: Scan self.UiParameter(...) Parser-->>Sync: Return inferred parameters<br/>name + AttributeType Sync->>Formly: Update operator uiParameters structure Formly->>ParamUI: Render parameter rows ParamUI-->>User: Shows inferred params<br/>name/type locked, value editable User->>ParamUI: Enters parameter values ParamUI->>Formly: Store edited values Formly->>OpDesc: Persist uiParameters in operator properties User->>OpDesc: Runs workflow OpDesc->>Injector: inject(code, uiParameters) Injector->>Injector: Validate uiParameters Injector->>Injector: Build parameter map Injector->>Injector: Generate reserved hook method Injector->>Injector: Insert hook into user UDF class Injector-->>OpDesc: Return injected Python code OpDesc->>Runtime: Execute OpExecWithCode(injectedCode, "python") Runtime->>UDF: Load user UDF class UDF->>Support: __init_subclass__ wraps open() Runtime->>UDF: Start operator execution UDF->>Support: wrapped open() runs first Support->>UDF: Call _texera_injected_ui_parameters() UDF-->>Support: Return UI parameter values Support->>Support: Store injected values Support->>UDF: Continue original user open() UDF->>Support: Create/read self.UiParameter(...) Support->>Support: Look up injected value by name Support->>Support: Convert value using AttributeType Support-->>UDF: Return typed Python value UDF->>Runtime: Process tuples / batches / source outputAny related issues, documentation, discussions?
Closes #4154
How was this PR tested?
Testing added to backend and frontend
Was this PR authored or co-authored using generative AI tooling?
Co-generated with GPT